#if !defined(__HTTP_SESSION_H__)
#    define __HTTP_SESSION_H__
#    include <boost/asio/dispatch.hpp>
#    include <boost/asio/ip/tcp.hpp>
#    include <boost/asio/ssl/stream.hpp>
#    include <boost/beast/core.hpp>
#    include <boost/beast/http.hpp>
#    include <boost/beast/ssl.hpp>
#    include <boost/beast/version.hpp>
#    include <boost/config.hpp>
#    include <boost/url.hpp>
#    include <boost/url/parse_query.hpp>
#    include <cstddef>
#    include <map>
#    include <request/HttpRequest.h>
#    include <string>

// namespace beast = boost::beast;   // from <boost/beast.hpp>
// namespace http = beast::http;     // from <boost/beast/http.hpp>
namespace rdhttp {

    class HttpSession : public boost::enable_shared_from_this<HttpSession> {
    public:
        using http_body         = boost::beast::http::string_body;
        using shared_ptr        = boost::shared_ptr<HttpSession>;
        using HttpRequest       = HttpRequest;
        using HttpResponse      = boost::beast::http::response<http_body>;
        using HttpRequestParser = boost::beast::http::request_parser<http_body>;
        using tcp_stream        = boost::beast::tcp_stream;
        using flat_buffer       = boost::beast::flat_buffer;
        using error_code        = boost::beast::error_code;
        static std::string server_name;
        typedef std::function<void(HttpRequest&, HttpResponse&)> RequestHandler;
        // typedef std::function<void(HttpSession::shared_ptr, HttpResponse&)> PosttHandler;
        typedef RequestHandler PosttHandler;

    private:
        tcp_stream m_stream;
        std::string const doc_root;
        static size_t const body_limit = 1024 * 1024 * 500;

        std::map<std::string, RequestHandler> get_handlers;
        std::map<std::string, PosttHandler> post_handlers;

        boost::optional<HttpRequestParser> m_parser;
        flat_buffer m_buffer;
        boost::optional<HttpRequest> request;

    public:
        HttpSession(boost::asio::ip::tcp::socket&& socket,
            std::string const& doc_root,
            std::map<std::string, RequestHandler>& get_handlers,
            std::map<std::string, PosttHandler>& post_handlers);
        ~HttpSession();

        void start();

        tcp_stream& stream();

        boost::optional<HttpRequestParser>& parser();
        flat_buffer& buffer();

    private:
        // Start the asynchronous operation
        void run();

        void do_read_header();

        void do_read_body();

        void on_read_header(
            boost::beast::error_code ec,
            std::size_t bytes_transferred);

        void on_read_body(
            boost::beast::error_code ec,
            std::size_t bytes_transferred);

        void read_done();

        void on_write(
            bool keep_alive,
            boost::beast::error_code ec,
            std::size_t bytes_transferred);

        void do_close();

        // Return a response for the given request.
        //
        // The concrete type of the response message (which depends on the
        // request), is type-erased in message_generator.
        boost::beast::http::message_generator
        handle_request();

        void send_response(boost::beast::http::message_generator&& msg);

    protected:
        /**
         * @brief Returns a server error responseF
         *
         * @param what
         * @return * HttpResponse const
         */
        HttpResponse server_error(boost::beast::string_view what) {
            boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::internal_server_error, m_parser->get().version()};
            res.set(boost::beast::http::field::server, server_name);
            res.set(boost::beast::http::field::content_type, "text/html; charset=UTF-8");
            res.keep_alive(m_parser->get().keep_alive());
            res.body() = "An error occurred: '" + std::string(what) + "'";
            res.prepare_payload();
            return res;
        };

        /**
         * @brief Returns a bad request response
         *
         * @param why
         * @return * HttpResponse
         */
        HttpResponse bad_request(boost::beast::string_view why) {
            boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::bad_request, m_parser->get().version()};
            res.set(boost::beast::http::field::server, server_name);
            res.set(boost::beast::http::field::content_type, "text/html; charset=UTF-8");
            res.keep_alive(m_parser->get().keep_alive());
            res.body() = std::string(why);
            res.prepare_payload();
            return res;
        };

        /**
         * @brief Returns a not found response
         *
         * @param target
         * @return HttpResponse
         */
        HttpResponse not_found(boost::beast::string_view target) {
            boost::beast::http::response<boost::beast::http::string_body> res{boost::beast::http::status::not_found, m_parser->get().version()};
            res.set(boost::beast::http::field::server, server_name);
            res.set(boost::beast::http::field::content_type, "text/html; charset=UTF-8");
            res.keep_alive(m_parser->get().keep_alive());
            res.body() = "The resource '" + std::string(target) + "' was not found.";
            res.prepare_payload();
            return res;
        };

        // HttpResponse
        // request_file();
    };

}  // namespace rdhttp
#endif  // __HTTP_SESSION_H__
